﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
using Commanding;
using EmlViewer.EmlViewerModel;
using EmlViewer.Helpers;
using EmlViewer.Properties;
using EmlViewer.Viewers;
namespace EmlViewer
{
    public class ViewModel : INotifyPropertyChanged
    {// note: code formatting done by Resharper
        private readonly Dispatcher _dispatcher;
        private string _errorCountText;
        private string _itemCountText;
        private static readonly object Locker = new object();
        private MessageInfo _vmSelectedMessageInfo;
        private List<MessageInfo> _underLyingMessageInfos = new List<MessageInfo>(); 
        public ViewModel()
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
            MessageInfos = new ObservableCollection<MessageInfo>();
            ExceptionInfos = new ObservableCollection<ExceptionInfo>();
            HelpAboutCommand = new RelayCommand(ExecuteHelpAboutCommand, CanExecuteHelpAboutCommand);
            LoadFilesCommand = new RelayCommand(ExecuteLoadFilesCommand, CanExecuteLoadFilesCommand);
            LoadDirectoriesCommand = new RelayCommand(ExecuteLoadDirectoriesCommand, CanExecuteLoadDirectoriesCommand);
            ViewContentCommand = new RelayCommand(ExecuteViewContentCommand, CanExecuteViewContentCommand);
            ClearCommand = new RelayCommand(ExecuteClearCommand, CanExecuteClearCommand);
            CloseChildWindowsCommand = new RelayCommand(ExecuteCloseChildWindowsCommand, CanExecuteCloseChildWindowsCommand);
            MessageInfos.CollectionChanged +=
                (s, a) =>
                {
                    lock (Locker)
                    {   // avoid space exception
                        _underLyingMessageInfos = MessageInfos.ToList();
                    }
                    ItemCountText = MiscHelpers.QuantityInflection(MessageInfos.Count, "item");
                    OnPropertyChanged("HaveItems");
                };
            ExceptionInfos.CollectionChanged +=
                (s, a) =>
                {
                    ErrorCountText = MiscHelpers.QuantityInflection(ExceptionInfos.Count, "exception");
                    OnPropertyChanged("HaveItems");
                };
            Task.Run(() => ProcessCommandTail());
        }
        public ViewerServices ViewerServices { get; set; }
        public Model Model { get; set; }
        public ICommand HelpAboutCommand { get; private set; }
        public ICommand LoadFilesCommand { get; private set; }
        public ICommand LoadDirectoriesCommand { get; private set; }
        public ICommand ViewContentCommand { get; set; }
        public ICommand ClearCommand { get; set; }
        public ICommand CloseChildWindowsCommand { get; set; }
        public ObservableCollection<MessageInfo> MessageInfos { get; set; }
        public ObservableCollection<ExceptionInfo> ExceptionInfos { get; set; }
        public MessageInfo VmSelectedMessageInfo
        {
            [DebuggerStepThrough] get { return _vmSelectedMessageInfo; }
            [DebuggerStepThrough]
            set
            {
                if (!(Equals(value, _vmSelectedMessageInfo)))
                {
                    _vmSelectedMessageInfo = value;
                    OnPropertyChanged("VmSelectedMessageInfo");
                }
            }
        }
        public string ItemCountText
        {
            [DebuggerStepThrough] get { return _itemCountText; }
            [DebuggerStepThrough]
            set
            {
                if (value != _itemCountText)
                {
                    _itemCountText = value;
                    OnPropertyChanged("ItemCountText");
                }
            }
        }
        public string ErrorCountText
        {
            [DebuggerStepThrough] get { return _errorCountText; }
            [DebuggerStepThrough]
            set
            {
                if (value != _errorCountText)
                {
                    _errorCountText = value;
                    OnPropertyChanged("ErrorCountText");
                }
            }
        }
        public bool HaveItems
        {
            get { return MessageInfos.Count > 0 || ExceptionInfos.Count > 0; }
        }
        #region Commanding
        private void ExecuteCloseChildWindowsCommand(object obj)
        {
            ViewerServices.CloseChildWindows();
        }
        private bool CanExecuteCloseChildWindowsCommand(object obj)
        {
            if (ViewerServices.WindowsCount > 0) return true;
            return false;
        }
        private void ExecuteClearCommand(object obj)
        {
            MessageInfos.Clear();
            ExceptionInfos.Clear();
        }
        private bool CanExecuteClearCommand(object obj)
        {
            if (MessageInfos.Count > 0) return true;
            return false;
        }
        private void ExecuteViewContentCommand(object obj)
        {
            try
            {
                string body;
                if (Model.TryGetHtml(VmSelectedMessageInfo, out body))
                {
                    ViewerServices.DisplayHtml(VmSelectedMessageInfo, body);
                }
                else if (Model.TryGetText(VmSelectedMessageInfo, out body))
                {
                    ViewerServices.DisplayHtml(VmSelectedMessageInfo, MiscHelpers.WrapForHtml(body));
                }
                else
                {
                    ViewerServices.Notify("Cannot locate the content");
                }
            }
            catch (Exception ex)
            {
                ExceptionInfos.Add(new ExceptionInfo
                {
                    Message = ex.Message,
                    Source = VmSelectedMessageInfo.ToString()
                });
            }
        }
        private bool CanExecuteViewContentCommand(object obj)
        {
            if (VmSelectedMessageInfo == null) return false;
            return true;
        }
        private void ExecuteLoadFilesCommand(object obj)
        {
            var t = new Task(() =>{
                ViewerServices.BeginWait(_dispatcher);
                LoadFilesWorker(obj);
            });
            t.ContinueWith(x => ViewerServices.EndWait(_dispatcher));
            t.Start();
        }
        private void LoadFilesWorker(object obj)
        {
            try
            {
                int beepCount = 0;
                var fileNames = obj as List<string>;
                if (fileNames != null)
                {
                    foreach (string name in fileNames)
                    {
                        string extension = Path.GetExtension(name);
                        if (extension != null)
                        {
                            string ext = extension.ToLower();
                            if (ext == Settings.Default.EmlExtension)
                            {
                                object o = Model.Load(name);
                                if (o is MessageInfo)
                                {
                                    MessageInfo mi = o as MessageInfo;
                                    if (_underLyingMessageInfos.ToList().FirstOrDefault(q => q.Id == mi.Id) == null)
                                    {
                                        _dispatcher.InvokeAsync(() => MessageInfos.Add(mi));
                                        continue;
                                    }
                                }
                                else if (o is Exception)
                                {
                                    var ex = o as Exception;
                                    string name1 = name;
                                    _dispatcher.InvokeAsync(() => ExceptionInfos.Add(new ExceptionInfo {Message = ex.Message, Source = name1}));
                                }
                            }
                        }
                        if (beepCount < 2)
                        {
                            Console.Beep();
                            ++beepCount;
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                _dispatcher.InvokeAsync(() => ExceptionInfos.Add(new ExceptionInfo {Message = exception.Message, Source = "Message loader"}));
            }
        }
        private bool CanExecuteLoadFilesCommand(object obj)
        {
            return true;
        }
        private void ExecuteLoadDirectoriesCommand(object obj)
        {
            try
            {
                var directoryNameList = obj as List<string>;
                if (directoryNameList != null)
                {
                    foreach (string directoryName in directoryNameList)
                    {
                        List<string> fileNames =
                            Directory.EnumerateFiles(directoryName, "*" + Settings.Default.EmlExtension).ToList();
                        if (fileNames.Count > 0)
                        {
                            ExecuteLoadFilesCommand(fileNames);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                ExceptionInfos.Add(new ExceptionInfo {Message = exception.Message, Source = "Message loader"});
            }
        }
        private bool CanExecuteLoadDirectoriesCommand(object obj)
        {
            return true;
        }
        private void ExecuteHelpAboutCommand(object obj)
        {
            ViewerServices.DisplayHelpAbout();
        }
        private bool CanExecuteHelpAboutCommand(object obj)
        {
            if (ViewerServices.HelpIsOpen) return false;
            return true;
        }
        #endregion
        #region Command tail
        private void ProcessCommandTail()
        {
            try
            {
                int sleeps = 0;
                while (Model == null || ViewerServices==null)
                {
                    Thread.Sleep(500);
                    if (++sleeps > 2) return;
                }
                string[] env = Environment.GetCommandLineArgs();
                if (env.Length > 1)
                {
                    string cmdTail = String.Join(" ", env, 1, env.Length - 1);
                    if (File.Exists(cmdTail))
                    {
                        var cmdArgs = new List<string> {cmdTail};
                        LoadFilesWorker(cmdArgs);
                        if (MessageInfos.Count > 0)
                        {
                            VmSelectedMessageInfo = MessageInfos[0];
                            ExecuteViewContentCommand(null);
                            ViewerServices.Minimize();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionInfos.Add(new ExceptionInfo
                {
                    Message = "Invalid command line argument: " + ex.Message,
                    Source = "Command line parser"
                });
            }
        }
        #endregion
        #region INotifyPropertyChanged Implementation
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = Interlocked.CompareExchange(ref PropertyChanged, null, null);
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
    }
}